package com.qfedu.fmmall.service.impl;

import com.qfedu.fmmall.common.status.ResStatus;
import com.qfedu.fmmall.common.utils.PageHelper;
import com.qfedu.fmmall.common.vo.ResultVO;
import com.qfedu.fmmall.dao.OrderItemMapper;
import com.qfedu.fmmall.dao.OrdersMapper;
import com.qfedu.fmmall.dao.ProductSkuMapper;
import com.qfedu.fmmall.dao.ShoppingCartMapper;
import com.qfedu.fmmall.entity.OrderItem;
import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.entity.ProductSku;
import com.qfedu.fmmall.service.OrdersService;
import com.qfedu.fmmall.vo.OrdersVO;
import com.qfedu.fmmall.vo.ShoppingCartVO;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author : yanBin
 * @date :2021/12/20 17:14
 */
@Service
public class OrdersServiceImpl implements OrdersService {

    private static final Logger log = LoggerFactory.getLogger(OrdersServiceImpl.class);

    @Resource
    private ShoppingCartMapper shoppingCartMapper;

    @Resource
    private OrdersMapper ordersMapper;

    @Resource
    private OrderItemMapper orderItemMapper;

    @Resource
    private ProductSkuMapper productSkuMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Transactional
    public Map<String, String> addOrders(String cids, Orders orders) {
        Map<String, String> map = new HashMap<>();
        //查询购物车记录
        String[] split = cids.split(",");
        List<Integer> list = new ArrayList<>();
        for (String s : split) {
            list.add(Integer.valueOf(s));
        }
        List<ShoppingCartVO> shoppingCartVOList = shoppingCartMapper.selectShoppingCartByCids(list);

        //从购物车信息中获取要刚买的skuId，以skuId为key做redisson分布式锁
        List<String> skuIds = new ArrayList<>();
        Map<String, RLock> lockMap = new HashMap<>();
        Boolean isLock = true;
        for (ShoppingCartVO shoppingCartVO : shoppingCartVOList) {
            if (shoppingCartVO != null && shoppingCartVO.getSkuId() != null) {
                //获取公平锁（按先后顺序获取所）
                //RLock fairLock = redissonClient.getFairLock(shoppingCartVO.getSkuId());
                //获取非公平锁（不管来的先后顺序）
                RLock lock = redissonClient.getLock(shoppingCartVO.getSkuId());
                //上锁 true表示上锁成功 false表示失败
                boolean b = false;

                try {
                    //阻塞锁
                    //lock.lock();
                    //非阻塞锁
                    b = lock.tryLock(20, 8, TimeUnit.SECONDS);
                    if (b) {
                        lockMap.put(shoppingCartVO.getSkuId(), lock);
                        skuIds.add(shoppingCartVO.getSkuId());
                    }
                } catch (Exception e) {
                    log.error(e.getLocalizedMessage());
                    e.printStackTrace();
                }
                isLock = isLock && b;
            }
        }
        //如果有没有上锁成功的订单，则释放所有这组已经上成功的锁


        /**
         * 用传统redis做分布式锁，缺点在集群环境不能把redis锁一步查删造成查删空隙，用其他办法可以补全好，但比较麻烦。所以一般用redisson
         //从购物车信息中获取要刚买的skuId，以skuId为key写入到redis中
         Boolean isLock = true;
         List<String> skuIds = new ArrayList<>();
         Map<String, String> values = new HashMap<>();
         for (ShoppingCartVO shoppingCartVO : shoppingCartVOList) {
         String value = UUID.randomUUID().toString();
         //加锁时一定要设置时间防止jvm突然挂机，造成死锁现象
         Boolean ss = stringRedisTemplate.boundValueOps(shoppingCartVO.getSkuId()).setIfAbsent(value, 5, TimeUnit.SECONDS);
         if (ss) {
         values.put(shoppingCartVO.getSkuId(), value);
         skuIds.add(shoppingCartVO.getSkuId());
         }
         isLock = isLock && ss;
         }
         //表示加锁失败执行if语句里面的
         if (!isLock) {
         for (String skuId : skuIds) {
         String s = null;
         if (StringUtils.isNotBlank(skuId)) {
         //防止可能自己的锁过期了，删除其他线程的锁
         s = stringRedisTemplate.boundValueOps(skuId).get();
         }
         if (StringUtils.isNotEmpty(s) || s.equals(values.get(skuId))) {
         //加锁失败有一些商品可能加锁成功，要释放这些商品的redis锁
         stringRedisTemplate.delete(skuId);
         }

         }

         return null;
         }
         */
        //加锁成功执行
        try {
            if (!isLock) {
                return null;
            }
            /**在集群环境当第一次查询完成过后，可能有节点操作数据库修改数据，释放redis锁。
             加锁成功数据库数据与第一次查询数据有差别,所以在加搜索一次数据库。
             */
            List<ShoppingCartVO> shoppingCartVOS = shoppingCartMapper.selectShoppingCartByCids(list);
            Boolean f = true;
            StringBuffer untitled = new StringBuffer();
            //判断购物车所购商品是否有库存
            for (ShoppingCartVO sc : shoppingCartVOS) {
                if (Integer.valueOf(sc.getCartNum()) > sc.getSkuStock()) {
                    f = false;
                }
                untitled.append(sc.getProductName());
                untitled.append(",");

            }
            if (!f) {
                ResultVO resultVO = new ResultVO(ResStatus.NO.ordinal(), "库存不足", null);
                map.put("ResultVO", resultVO.toString());
                return map;
            }

            //添加到订单表orders
            orders.setUntitled(untitled.toString());
            orders.setCancelTime(new Date());
            orders.setStatus("1");
            String orderId = UUID.randomUUID().toString().replace("-", "");
            orders.setOrderId(orderId);
            int i = ordersMapper.insert(orders);
            if (i <= 0) {
                ResultVO resultVO = new ResultVO(ResStatus.NO.ordinal(), "创建订单失败", null);
                map.put("ResultVO", resultVO.toString());
                return map;
            }

            //添加到订单快照表order_item
            for (ShoppingCartVO sc : shoppingCartVOS) {
                int cnm = Integer.valueOf(sc.getCartNum());
                String itemId = UUID.randomUUID().toString().replace("-", "");
                OrderItem orderItem = new OrderItem(itemId, orderId, sc.getProductId(), sc.getProductName(),
                        sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new BigDecimal(sc.getSellPrice())
                        , cnm, new BigDecimal(cnm * sc.getSellPrice()), new Date(), new Date(), 0);
                int j = orderItemMapper.insert(orderItem);
                if (j <= 0) {
                    ResultVO resultVO = new ResultVO(ResStatus.NO.ordinal(), "创建订单快照失败", null);
                    map.put("ResultVO", resultVO.toString());
                    return map;
                }
            }

            //在sku表中减去商品库存
            for (ShoppingCartVO sc : shoppingCartVOS) {
                ProductSku productSku = new ProductSku();
                productSku.setStock(sc.getSkuStock() - Integer.valueOf(sc.getCartNum()));
                Example example = new Example(ProductSku.class);
                Example.Criteria criteria = example.createCriteria();
                criteria.andEqualTo("skuId", sc.getSkuId());
                int i1 = productSkuMapper.updateByExampleSelective(productSku, example);
                if (i1 <= 0) {
                    ResultVO resultVO = new ResultVO(ResStatus.NO.ordinal(), "修改仓库数量失败", null);
                    map.put("ResultVO", resultVO.toString());
                    return map;
                }
            }

            //删除购物车记录
            for (Integer cid : list) {
                int i1 = shoppingCartMapper.deleteByPrimaryKey(cid);
                if (i1 <= 0) {
                    ResultVO resultVO = new ResultVO(ResStatus.NO.ordinal(), "删除购物车失败", null);
                    map.put("ResultVO", resultVO.toString());
                    return map;
                }
            }
            map.put("orderId", orderId);
            map.put("productNames", untitled.toString());

        } catch (Exception e) {

            ResultVO resultVO = new ResultVO(ResStatus.NO.ordinal(), e.getLocalizedMessage(), null);
            map.put("resultVo", resultVO.toString());

        } finally {
            for (String skuId : skuIds) {
                if (StringUtils.isNotBlank(skuId)) {
                    //释放用redisson分布式锁
                    lockMap.get(skuId).unlock();
                    System.out.println("===================================分布式锁");
                }
            }
            /**
             * 用传统redis做分布式锁，缺点在集群环境不能把redis锁一步查删造成查删空隙，用其他办法可以补全好，但比较麻烦。所以一般用redisson
             for (String skuId : skuIds) {
             String s = null;
             if (StringUtils.isNotBlank(skuId)) {
             //防止可能自己的锁过期了，删除其他线程的锁
             s = stringRedisTemplate.boundValueOps(skuId).get();
             }
             if (StringUtils.isNotEmpty(s) || s.equals(values.get(skuId))) {
             //加锁失败有一些商品可能加锁成功，要释放这些商品的redis锁
             stringRedisTemplate.delete(skuId);
             }
             }
             */

        }

        return map;
    }

    @Override
    public int updateOrdersByStatus(String orderId, String status) {
        Orders orders = new Orders();
        orders.setOrderId(orderId);
        orders.setStatus(status);
        int i = ordersMapper.updateByPrimaryKeySelective(orders);
        return i;
    }

    @Override
    public ResultVO getOrderById(String orderId) {
        Orders orders = ordersMapper.selectByPrimaryKey(orderId);
        return new ResultVO(ResStatus.OK.ordinal(), "查询订单成功", orders.getStatus());
    }

    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void closeOrder(String orderId) {
        synchronized (this) {
            //2.4修改当前订单：status=6 已关闭 close_type=1超时未支付
            Orders orders1 = new Orders();
            orders1.setOrderId(orderId);
            orders1.setStatus("6");
            orders1.setCloseType(1);
            ordersMapper.updateByPrimaryKeySelective(orders1);
            //2.5还原库存
            Example example = new Example(OrderItem.class);
            Example.Criteria criteria1 = example.createCriteria();
            criteria1.andEqualTo("orderId", orderId);
            List<OrderItem> orderItems = orderItemMapper.selectByExample(example);
            for (OrderItem orderItem : orderItems) {
                ProductSku productSku = productSkuMapper.selectByPrimaryKey(orderItem.getSkuId());
                productSku.setStock(productSku.getStock() + orderItem.getBuyCounts());
                productSku.setUpdateTime(new Date());
                productSkuMapper.updateByPrimaryKey(productSku);
            }

        }

    }

    @Override
    public ResultVO listOrders(String userId, String status, int page, int limit) {
        limit = limit <= 0 ? 10 : limit;
        int start = (page - 1) * limit < 0 ? 0 : (page - 1) * limit;
        List<OrdersVO> ordersVOS = ordersMapper.selectOrders(userId, status, start, limit);

        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("userId", userId);
        if (StringUtils.isNotBlank(status)) {
            criteria.andEqualTo("status", status);
        }
        int count = ordersMapper.selectCountByExample(example);
        int pageCount = count % limit == 0 ? count / limit : count / limit + 1;
        PageHelper<OrdersVO> pageHelper = new PageHelper<OrdersVO>(count, pageCount, ordersVOS);
        return new ResultVO(ResStatus.OK.ordinal(), "用户查询订单成功", pageHelper);
    }


}
